#include "InterOpIO.h"
#include "SPAIopAssemblyPathPref.h"
#include "SPAIopEdgeAttributeMode.h"
#include "SPAIopImportOptionsCatiaV5.h"
#include "SPAIopOptionAssemblyUnfoldedViewMode.h"
#include "SPAIopPolicy.h"
#include "SPAIopPolicyUtilities.h"
#include "SPAIopTranslateSuppressedEntitiesMode.h"
#include "core/progress/InterOpProgressReporter.h"
#include "reader/InterOpReader.h"
#include "utils/FileUtil.h"
#include "utils/StringUtil.h"
#include "writer/InterOpWriter.h"
#include "SPAIopAcisSystem.h"
#include "common/logger.h"
#include <locale>

#include <future>
#include <iostream>
#include <memory>
#include <string>
#include "InterOpUtils.h"
#include "utils/ThreadPoolInstance.h"
#include "InterOpConvert.h"
#include "InterOpDocumentInstance.h"
#include "InterOpDocumentUtils.h"
#include <kernapi.hxx>
#include <nlohmann/json.hpp>
#include <unordered_map>
#include "InterOpConvertV2.h"
AMCAX_EXCHANGE_NAMESPACE_BEGIN

enum class FileAccessMode{
    Read,
    Write,
    ReadWrite,
    UNSUPPORT
} ;

std::unordered_map<std::string, FileAccessMode> FileAccessModeMap = {
    // 3dxml
    {".3dxml", FileAccessMode::ReadWrite},
    // catiav4
    {".model", FileAccessMode::ReadWrite},
    {".exp", FileAccessMode::ReadWrite},
    {".session", FileAccessMode::ReadWrite},
    // catiav5
    {".catpart", FileAccessMode::ReadWrite},
    {".catproduct", FileAccessMode::ReadWrite},
    {".cgr", FileAccessMode::ReadWrite},
    // dxf/dwg
    {".dxf", FileAccessMode::Read},
    {".dwg", FileAccessMode::Read},
    // iges
    {".iges", FileAccessMode::ReadWrite},
    {".igs", FileAccessMode::ReadWrite},
    // inventor
    {".ipt", FileAccessMode::Read},
    {".asm", FileAccessMode::Read},
    {".iam", FileAccessMode::Read},
    // jt
    {".jt", FileAccessMode::Read},
    // nx
    {".prt", FileAccessMode::Read},
    // parasolid
    {".xml_txt", FileAccessMode::Read},
    {".x_b", FileAccessMode::Read},
    {".x_t", FileAccessMode::Read},
    {".xmt_bin", FileAccessMode::Read},
    {".p_b", FileAccessMode::Read},
    {".xmp_bin", FileAccessMode::Read},
    {".xmp_txt", FileAccessMode::Read},
    // pro/e
    {".prt", FileAccessMode::Read},
    {".asm", FileAccessMode::Read},
    // rhino
    {".3dm", FileAccessMode::Read},
    // Solid Edge
    {".par", FileAccessMode::Read},
    {".asm", FileAccessMode::Read},
    {".psm", FileAccessMode::Read},
    // solid works
    {".sldprt", FileAccessMode::Read},
    {".sldasm", FileAccessMode::Read},
    // step
    {".stp", FileAccessMode::ReadWrite},
    {".step", FileAccessMode::ReadWrite},
    // stl
    {".stl", FileAccessMode::ReadWrite},
    // VDA-FS
    {".vda", FileAccessMode::ReadWrite},
};

bool CheckReadStatus(const std::string &filePath)
{
    std::string fileExtension = StringUtil::ToLowerCase(FileUtil::GetExtension(filePath));
    auto it = FileAccessModeMap.find(fileExtension);
    if (it != FileAccessModeMap.end())
    {
        return it->second == FileAccessMode::Read || it->second == FileAccessMode::ReadWrite;
    }
    return false;
}
bool CheckWriteStatus(const std::string &filePath)
{
    
    std::string fileExtension = StringUtil::ToLowerCase(FileUtil::GetExtension(filePath));
    auto it = FileAccessModeMap.find(fileExtension);
    if (it != FileAccessModeMap.end())
    {
        return it->second == FileAccessMode::Write || it->second == FileAccessMode::ReadWrite;
    }
    return false;
}

namespace InterOp
{

    SPAIopAcisSystemInitGuard *globalInitGuard{nullptr};
    // std::make_unique(ThreadPoolInstance)
    std::unique_ptr<ThreadPoolInstance> worker;
    

    IO::IO()
    {
        size_t threadPoolNums = std::thread::hardware_concurrency() / 2;
        // ThreadPoolManager::GetInstance().InitThreadPool(1) ;
        worker = std::make_unique<ThreadPoolInstance>(1);
    }

    IO &IO::GetInstance()
    {
        static IO instance;
        return instance;
    }

    bool IO::CheckLicense(const std::string &license)
    {
        if (!checkLicensePass)
        {
            checkLicensePass = InterOp::CheckLicense(license, globalInitGuard);
        }
        return checkLicensePass ;
    }

    void IO::InitLogger()
    {
        InitSpdlog();
    }

    bool Read(std::string filePath, std::vector<std::shared_ptr<InterOpObject>> *exchangeObjectList, std::shared_ptr<InterOp::InterOpDocumentInstance> *rootInterOpDocInstance,std::unordered_map<std::string,PMI::InterOpShapePMIInfos> * pmi)
    {
        LOG_INFO("thread pool parsing");
        Reader reader(filePath);
        reader.SetExchangeObjectList(exchangeObjectList);
        //reader.SetExchangeRootInstance(rootInterOpDocInstance);
        reader.ReadFromFilePath();
        *rootInterOpDocInstance = reader.GetExchangeRootInstance();
        for(auto it: reader.GetPmiInfo()){
            pmi->insert({it.first,it.second}) ;
        }
        return true;
    }

    void IO::ReadFromFilePath(std::string filePath, std::vector<std::shared_ptr<InterOpObject>> *exchangeObjectList, std::shared_ptr<InterOp::InterOpDocumentInstance> *rootInterOpDocInstance)
    {

        if (!checkLicensePass)
        {
            LOG_ERROR("licence check failed");
            return;
        }
        Reader reader(filePath) ;
        reader.SetExchangeObjectList(exchangeObjectList);
        reader.ReadFromFilePath();
        *rootInterOpDocInstance = reader.GetExchangeRootInstance();
        for(auto it: reader.GetPmiInfo()){
            pmi_.insert({it.first,it.second}) ;
        }
        
        // uintmax_t size =std::filesystem::file_size(filePath);
        // double fileSizeMB = static_cast<double>(size) / (1024.0 * 1024.0);
        
        // LOG_INFO("send to threadpool");
        // std::future<bool> future = worker->AddWorker(Read, filePath, exchangeObjectList, rootInterOpDocInstance,&pmi_);

        // future.get();
        LOG_INFO("read done");
    }

    void IO::WriterToFile(std::string filePath, const std::vector<std::shared_ptr<InterOpObject>> &exchange_shapes, const std::shared_ptr<InterOpDocumentInstance> &docRootInstance)
    {
        if (!checkLicensePass)
        {
            LOG_ERROR("licence check failed");
            return;
        }

        // TODO: to be updated.
        Writer writer;
        //writer.WriterToFile(filePath, exchange_shapes);
        writer.WriteToFile2(filePath, docRootInstance);
        LOG_INFO("write done");
    }

    void IO::Convert(const std::string &inFilePath, const std::string &outFilePath, const std::string &policyFilePath) 
    {
        #ifdef __linux__
        std::locale::global(std::locale("en_US.UTF-8"));
        #endif

        if(!CheckReadStatus(inFilePath))
        {
            LOG_ERROR("The input file {} is not readable.", inFilePath);
            return ;
        }
        if(!CheckWriteStatus(outFilePath))
        {
            LOG_ERROR("The output file {} is not writable.", outFilePath);
            return ;
        }
        // LOG_INFO("Convert {} to {}",inFilePath,outFilePath) ;
        // ConvertUseInterOp(inFilePath,outFilePath,policyFilePath);
        std::string absoluteInFilePath = FileUtil::GetAbsolutePath(inFilePath) ;
        std::string absoluteOutFilePath = FileUtil::GetAbsolutePath(outFilePath) ;
        ImportExport::ConvertUseInterOp(absoluteInFilePath,absoluteOutFilePath,policyFilePath) ;
    }

    MetaDataInfo IO::GetMetaData(const std::string &inFilePath, const std::string &outTopoJsonFilePath, const FacetOptions &facetOptions) const
    {
        if(!CheckReadStatus(inFilePath))
        {
            LOG_ERROR("The input file {} is not readable.", inFilePath);
            return MetaDataInfo();
        }        
        return ImportExport::GetMetaDataUsingInterOp(inFilePath, outTopoJsonFilePath, facetOptions) ;
    }    


    void IO::GetDocumentInfo(const std::string &inFilePath, std::string &outJson)
    {
        // api_start_modeller(0);   
        if(!CheckReadStatus(inFilePath))
        {
            LOG_ERROR("The input file {} is not readable.", inFilePath);
            return ;
        }

        try
        {
            SPAIopWString wSourceFilePath = AMCAX::Exchange::InterOp::StringToSPAIopWString(inFilePath);
            SPAIopDocument sourceDoc(wSourceFilePath);

            // access document information
            SPAIopDocumentInformation docInfo = sourceDoc.GetInformation();
            SPAIopDocumentType docType = SPAIopDocumentType_Unknown;
            docInfo.GetType(docType);

            nlohmann::json outputJsonObject;
            AMCAX::InterOpDocumentUtils::AccessInformation(wSourceFilePath, docInfo, docType == SPAIopDocumentType::SPAIopDocumentType_Assembly, outputJsonObject);
            std::cout << outputJsonObject << std::endl ;
            LOG_INFO("outjson: {}",outJson) ;
            outJson = outputJsonObject.dump();
            LOG_INFO("outjson: {}",outJson) ;
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        
        // api_stop_modeller();
    }

    void IO::GetProperties(const std::string &inFilePath, std::string &outJson, bool includingChildren)
    {
        // api_start_modeller(0);   

        if(!CheckReadStatus(inFilePath))
        {
            LOG_ERROR("The input file {} is not readable.", inFilePath);
            return ;
        }

        try
        { 
            SPAIopWString wSourceFilePath = AMCAX::Exchange::InterOp::StringToSPAIopWString(inFilePath);
            nlohmann::json outputJsonObject;

            InterOpDocumentUtils::AccessProperties(wSourceFilePath, outputJsonObject, includingChildren);

            outJson = outputJsonObject.dump();
        }
        catch(const std::exception& e)
        {
            std::cerr << e.what() << '\n';
        }
        
        // api_stop_modeller();
    }

    std::unordered_map<std::string,PMI::InterOpShapePMIInfos> IO::GetPMIInfo(const std::string &inFilePath)
    {
        std::vector<std::shared_ptr<InterOpObject>> exchangeObjectList;
        std::shared_ptr<AMCAX::Exchange::InterOp::InterOpDocumentInstance> rootInterOpDocInstance ;
        ReadFromFilePath(inFilePath,&exchangeObjectList,&rootInterOpDocInstance) ;
        return pmi_ ;
    }

    void BuildCatiaV5Options(SPAIopPolicy &policy)
    {
        SPAIopImportOptionsCatiaV5 catiaV5Options;
        SPAIopWString temp(L"");
        catiaV5Options.SetAssemblyPathSearchStrategy(temp,SPAIopAssemblyPathPrefType::SPAIopAssemblyPathPrefType_Unknown) ;
        catiaV5Options.SetAssemblyUnfoldedViewMode(SPAIopOptionAssemblyUnfoldedViewMode::SPAIopOptionAssemblyUnfoldedViewMode_False) ;
        catiaV5Options.SetEdgeAttributesMode(SPAIopEdgeAttributeModeType::SPAIopEdgeAttributeModeType_None) ;
        catiaV5Options.SetFileVersionSupportMode(	SPAIopFileVersionSupportModeType::SPAIopFileVersionSupportMode_CommercialOnly) ;
        catiaV5Options.SetMultiRepresentationSurfacesMode(SPAIopMultiRepresentationSurfacesModeType::SPAIopMultiRepresentationSurfacesMode_Canonical) ;
        catiaV5Options.SetNameProcessingMode(SPAIopNameProcessingModeType::SPAIopNameProcessingMode_LongNames) ;
        catiaV5Options.SetPersistentNameProcessingMode(SPAIopPersistentNameProcessingModeType_Unknown) ;
        catiaV5Options.SetProcessInWorkObject(SPAIopProcessInWorkObjectModeType::SPAIopProcessInWorkObjectMode_True) ;
        catiaV5Options.SetProcessMfgDataMode(SPAIopProcessMfgDataModeType::SPAIopProcessMfgDataMode_All) ;
        catiaV5Options.SetRevolvedSurfacesMode(SPAIopRevolvedSurfacesModeType::SPAIopRevolvedSurfacesMode_NUBS) ;
        catiaV5Options.SetTranslateFaceNameMode(SPAIopTranslateFaceNameModeType::SPAIopTranslateFaceNameMode_OwningFeature) ;
        catiaV5Options.SetTranslateHiddenEntitiesMode(SPAIopTranslateHiddenEntitiesModeType::SPAIopTranslateHiddenEntitiesModeType_All) ;
        catiaV5Options.SetTranslateSuppressedEntitiesMode(SPAIopTranslateSuppressedEntitiesModeType::SPAIopTranslateSuppressedEntitiesMode_All) ;
        catiaV5Options.SetVisuPMIProcessingMode(SPAIopVisuPMIProcessingMode::SPAIopVisuPMIProcessingMode_All) ;
        policy.AddOrUpdateImportOptions(catiaV5Options);
    }

    void BuildIgesOptions(SPAIopPolicy &policy){
        SPAIopImportOptionsIges igesOptions;
        igesOptions.SetCopiousDataMode (SPAIopCopiousDataModeType::SPAIopCopiousDataMode_SingleBspline) ;
        igesOptions.SetImportEntities (SPAIopOptionImportEntities::SPAIopImportEntities_RootOnly) ;
        igesOptions.SetTranslateHiddenEntitiesMode (SPAIopTranslateHiddenEntitiesModeType::SPAIopTranslateHiddenEntitiesModeType_All) ;
 
        igesOptions.SetTrimCurvePreferenceMode (SPAIopTrimCurveImportPreferenceModeType::SPAIopTrimCurveImportPreferenceMode_FromSource) ;
        policy.AddOrUpdateImportOptions(igesOptions);
    }

    void BuildParasolidOptions(SPAIopPolicy &policy){
        SPAIopImportOptionsParasolid parasolidOptions;
        parasolidOptions.SetImportUnits (SPAIopLengthUnitType::SPAIopLengthUnit_Unknown) ;
    
 
        parasolidOptions.SetTranslateHiddenEntitiesMode (SPAIopTranslateHiddenEntitiesModeType::SPAIopTranslateHiddenEntitiesModeType_All)  ;
        policy.AddOrUpdateImportOptions(parasolidOptions);
    }

    void BuildSolidWorksOptions(SPAIopPolicy &policy) 
    {
        SPAIopImportOptionsSolidWorks solidWorksOptions ;
        SPAIopWString temp(L"path1;path2;path3") ;
        solidWorksOptions.SetAssemblyPathSearchStrategy (temp, SPAIopAssemblyPathPrefType::SPAIopAssemblyPathPrefType_Unknown) ;
        solidWorksOptions.SetFileVersionSupportMode (SPAIopFileVersionSupportModeType::SPAIopFileVersionSupportMode_CommercialOnly) ;
 
        SPAIopWString configName(L"") ;
        solidWorksOptions.SetImportConfiguration (configName) ;
 
        solidWorksOptions.SetImportProductStructureMode (SPAIopImportProductStructureMode::SPAIopImportProductStructureMode_From_AsmRep) ;
 
        solidWorksOptions.SetPersistentNameProcessingMode (SPAIopPersistentNameProcessingModeType_Unknown) ;
 
        solidWorksOptions.SetTranslateHiddenEntitiesMode (SPAIopTranslateHiddenEntitiesModeType::SPAIopTranslateHiddenEntitiesModeType_All) ;
 
        solidWorksOptions.SetTranslateSuppressedEntitiesMode (SPAIopTranslateSuppressedEntitiesModeType ::SPAIopTranslateSuppressedEntitiesMode_All) ;
 
        solidWorksOptions.SetVisuPMIProcessingMode (SPAIopVisuPMIProcessingMode::SPAIopVisuPMIProcessingMode_All) ;

        policy.AddOrUpdateImportOptions(solidWorksOptions) ;
    }

    void BuildStepOptions(SPAIopPolicy &policy) 
    {
        SPAIopImportOptionsStep stepOptions;

        stepOptions.SetImportClassIVBodyMode (SPAIopImportBodyTopologyModeType::SPAIopImportBodyTopologyMode_Preserve) ;

        stepOptions.SetVisuPMIProcessingMode (SPAIopVisuPMIProcessingMode::SPAIopVisuPMIProcessingMode_All)  ;
        stepOptions.SetLayerNameProcessingMode(SPAIopLayerNameProcessingModeType::SPAIopLayerNameProcessingMode_AsName) ;;
        stepOptions.SetFacetedBodyMode(SPAIopImportFacetedBodyMode::SPAIopImportFacetedBodyMode_BRep) ;
        
        policy.AddOrUpdateImportOptions(stepOptions);
    }

    void IO::ExportPolicy(const std::string &optionsFilePath) 
    {
        std::string parentPath = FileUtil::GetParentPath(optionsFilePath);
        if (!FileUtil::IsDirExist(parentPath))
        {
            LOG_ERROR("The parent path of the options file does not exist.");
            throw std::runtime_error("The parent path of the options file does not exist.");
        }
        SPAIopPolicy policy;
        SPAIopWString outputPolicyFilePath = AMCAX::Exchange::InterOp::StringToSPAIopWString(optionsFilePath);
        BuildCatiaV5Options(policy) ;
        BuildIgesOptions(policy) ;
        BuildParasolidOptions(policy) ; 
        BuildSolidWorksOptions(policy) ;
        BuildStepOptions(policy) ;
        SPAIopPolicyUtilities::GeneratePolicyFile(policy, outputPolicyFilePath);
    }

    void IO::SetReporter(InterOp::Reporter::ReporterType reporter) 
    {
        InterOp::Reporter::InterOpProgressReporter::GetInstance().SetReporterProcess(reporter) ;
    }

}

AMCAX_EXCHANGE_NAMESPACE_END
